﻿using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using ChanTai.WebApi.Exceptions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NVelocity;
using NVelocity.App;
using NVelocity.Context;
using NVelocity.Runtime;

namespace ChanTai.WebApi.Tpl
{
    /// <summary>
    /// 模板解析
    /// </summary>
    public class ApiEngine
    {
        private IContext _context;
        private VelocityEngine _velocity;
        private string _templateName;
        private readonly string _inEncoding;
        private readonly string _outEncoding;
        private HttpClient _http;

        private static readonly string Logpath;
        private static readonly int Loglevel;
        static ApiEngine()
        {
            Logpath = System.Configuration.ConfigurationManager.AppSettings.Get("log");
            if (string.IsNullOrEmpty(Logpath)) Logpath = Path.Combine(Logpath, "log");
            if (!Logpath.Contains(":")) Logpath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Logpath);
            if (!Directory.Exists(Logpath)) Directory.CreateDirectory(Logpath);
            if (!int.TryParse(System.Configuration.ConfigurationManager.AppSettings.Get("LogLevel"), out Loglevel)) Loglevel = 0xF;
        }

        /// <summary>
        /// 保存日志
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="dt">报文数据</param>
        /// <param name="author">授权客户信息</param>
        /// <param name="req">是否请求</param>
        /// <param name="api">请求类型</param>
        static void Log(string url,string api , string dt, bool req = true, string author ="")
        {
            if ((Loglevel & 2) == 0) return;
            if (string.IsNullOrEmpty(dt)) return;
            var type = req ? "Request" : "Response";
            var path = Logpath;
            if(!string.IsNullOrEmpty(author)) path = Path.Combine(Logpath, author);
            if (!Directory.Exists(path)) Directory.CreateDirectory(path);
            using (var logfile = new StreamWriter(File.OpenWrite($"{Path.Combine(path, $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}-{api}_{type}.txt")}")))
            {
                logfile.WriteLine($"URL:{url}");
                try
                {
                    var dc=new XmlDocument();
                    dc.LoadXml(dt);
                    //var fdt=new StringBuilder();
                    using (var ms=new MemoryStream())
                    using (var st = new StreamWriter(ms, Encoding.UTF8))
                    using (var ret=new StreamReader(ms, Encoding.UTF8))
                    {
                        dc.Save(st);
                        ms.Seek(0, SeekOrigin.Begin);
                        dt = ret.ReadToEnd();
                    }
                }
                catch 
                {
                }
                logfile.WriteLine($"{dt}");
            }
        }

        /// <summary>  
        ///  构造函数  
        /// </summary>  
        /// <param name="templatDir">模板文件夹路径</param>  
        /// <param name="templateName">模板文件名</param>
        /// <param name="inCode">输入文件编码</param>
        /// <param name="outCode">输出文件编码</param>  
        public ApiEngine(string templatDir, string templateName,string inCode,string outCode= "utf-8")
        {
            Init(templatDir);
            _templateName = templateName;
            _inEncoding = inCode;
            _outEncoding = outCode;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="templateName">模板文件名</param>
        public ApiEngine(string templateName) :this(AppDomain.CurrentDomain.BaseDirectory, templateName,"utf-8","utf-8")
        {
            _templateName = templateName;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public ApiEngine()
        {
            _inEncoding = _outEncoding = "utf-8";
        }
        /// <summary>  
        /// 设置模板文件夹  
        /// </summary>  
        /// <param name="templatDir"></param>  
        public void SetTemplateDirPath(string templatDir)
        {
            Init(templatDir);
        }
        /// <summary>  
        /// 设置模板文件  
        /// </summary>  
        /// <param name="templateName"></param>  
        public void SetTemplateFileName(string templateName)
        {
            _templateName = templateName;
        }


        /// <summary>  
        ///     初始化NVelocity模块  
        /// </summary>  
        /// <param name="templatDir">模板文件夹路径</param>  
        public void Init(string templatDir)
        {
            //创建VelocityEngine实例对象并设置初始化VelocityEngine   
            _velocity = new VelocityEngine();
            _velocity.SetProperty(RuntimeConstants.RESOURCE_LOADER, "file");
            _velocity.SetProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, templatDir);
            _velocity.SetProperty(RuntimeConstants.INPUT_ENCODING, _inEncoding);
            _velocity.SetProperty(RuntimeConstants.OUTPUT_ENCODING, _outEncoding);
            _velocity.Init();

            //为模板变量赋值  
            _context = new VelocityContext();

            _http = new HttpClient();
        }

        /// <summary>  
        /// 给模板变量赋值  
        /// </summary>  
        /// <param name="key">模板变量</param>  
        /// <param name="value">模板变量值</param>  
        public void Put(string key, object value)
        {
            if (_context == null)
            {
                _context = new VelocityContext();
            }
            _context.Put(key, value);
        }

        /// <summary>  
        /// 渲染模板
        /// </summary>   
        public string Render()
        {
            if (string.IsNullOrEmpty(_templateName)) throw new Exception("未指定模板文件");
            //从文件中读取模板  
            var template = _velocity.GetTemplate(_templateName);
            //合并模板  
            var writer = new StringWriter();
            template.Merge(_context, writer);
            return writer.GetStringBuilder().ToString();
        }

        /// <summary>
        /// 请求API
        /// </summary>
        /// <returns></returns>
        public JObject Post()
        {
            var req = Render();
            var cot = new StringContent(req, Encoding.UTF8);
            cot.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
            var url = $"{_context.Get("url")}";
            if(string.IsNullOrEmpty(url)) throw new Exception("API<url>配置错误");
            var api = $"{_context.Get("api")}";
            var author = $"{_context.Get("author")}";
            Log(url,api, req,true,author);
            var ret = _http.PostAsync(url, cot);
            var xmlstr = ret.Result.Content.ReadAsStringAsync().Result;
            var fxmlstr = Regex.Match(xmlstr, "<responseBody>(.+)</responseBody>").Value;
            fxmlstr = fxmlstr.Replace("responseBody", "Result");
            fxmlstr = fxmlstr.Replace("response", "");
            fxmlstr = fxmlstr.Replace("return", "Body");
            Log(url, api, xmlstr, false, author);
            Log(url, $"{api}#", fxmlstr, false, author);
            var dc = new XmlDocument();
            dc.LoadXml(fxmlstr);
            var jret =JObject.Parse(JsonConvert.SerializeXmlNode(dc));
            dynamic dret = jret;
            if ($"{dret.Result.Head.Code}" != "0000") throw new WarningException($"{dret.Result.Head.errorMessage}");
            return jret;
        }
    }
}
